﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;

using LeanEngine.Extensions;
using LeanEngine.Exception;

namespace LeanEngine.Business
{
    /// <summary>
    /// Represents a generic business rules.
    /// </summary>
    public abstract class BusinessRule<TParam, TResult> : IBusinessRule
        where TParam : class
        where TResult : class
    {
        #region Abstract Members

        /// <summary>
        /// Gets the error counter category
        /// </summary>
        public abstract DiagnosticCategoryItem ErrorCounter { get; }

        /// <summary>
        /// Executes a business rule according to a use case.
        /// </summary>
        /// <param name="value">The instance of the LeanEngine.IDataTransferObject for apply the business rules.</param>
        /// <param name="errorMessageContainer">
        /// The container of all errors messages obtained after the executing of the business rule 
        /// separated by a Environment.NewLine character.
        /// </param>
        protected abstract TResult ExecuteRule(TParam value);

        #endregion

        #region Method Members

        /// <summary>
        /// Executes the business rule.
        /// </summary>
        /// <exception cref="System.ArgumentNullException"></exception>
        /// <exception cref="BusinessException"></exception>
        /// <param name="value">The instance of the <typeparamref name="TParam"/> for apply the business rules.</param>
        public TResult Execute(TParam value)
        {
            if (value == null) throw new ArgumentNullException("value");

            try
            {
                return ExecuteRule(value);
            }
            catch (System.Exception ex)
            {
                var businessException = ex as BusinessException;
                if (businessException == null)
                {
                    CrashLogger.Log(ex, value, "Teste");
                }

                if (this.ErrorCounter.IsNotNull())
                {
                    this.ErrorCounter.Increment();
                }

                throw ex;
            }
        }

        #endregion

        #region IBusinessRule Members

        /// <summary>
        /// 
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public object Execute(object value)
        {
            return this.Execute(value as TParam);
        }

        #endregion
    }

    /// <summary>
    /// Represents a generic business rules.
    /// </summary>
    public abstract class BusinessRule<TParam> : IBusinessRule
        where TParam : class
    {
        #region Abstract Members

        /// <summary>
        /// Gets the error counter category
        /// </summary>
        public abstract DiagnosticCategoryItem ErrorCounter { get; }

        /// <summary>
        /// Executes a business rule according to a use case.
        /// </summary>
        /// <param name="value">The instance of the LeanEngine.IDataTransferObject for apply the business rules.</param>
        /// <param name="errorMessageContainer">
        /// The container of all errors messages obtained after the executing of the business rule 
        /// separated by a Environment.NewLine character.
        /// </param>
        protected abstract void ExecuteRule(TParam value);

        #endregion

        #region Method Members

        /// <summary>
        /// Executes the business rule.
        /// </summary>
        /// <exception cref="System.ArgumentNullException"></exception>
        /// <exception cref="BusinessException"></exception>
        /// <param name="value">The instance of the <typeparamref name="TParam"/> for apply the business rules.</param>
        public void Execute(TParam value)
        {
            if (value == null) throw new ArgumentNullException("value");

            try
            {
                ExecuteRule(value);
            }
            catch (System.Exception ex)
            {
                var businessException = ex as BusinessException;
                if (businessException == null)
                {
                    CrashLogger.Log(ex, value, "Teste");
                }

                if (this.ErrorCounter.IsNotNull())
                {
                    this.ErrorCounter.Increment();
                }

                throw ex;
            }
        }

        #endregion

        #region IBusinessRule Members

        /// <summary>
        /// 
        /// </summary>
        /// <param name="value"></param>
        /// <returns>Returns null.</returns>
        public object Execute(object value)
        {
            this.Execute(value as TParam);
            return null;
        }

        #endregion
    }

    /// <summary>
    /// Represents a generic business rule.
    /// </summary>
    public interface IBusinessRule
    {
        /// <summary>
        /// Gets the error counter category
        /// </summary>
        DiagnosticCategoryItem ErrorCounter { get; }

        /// <summary>
        /// Executes the business rule.
        /// </summary>
        /// <exception cref="System.ArgumentNullException"></exception>
        /// <exception cref="BusinessException"></exception>
        /// <param name="value">The values for apply the business rules.</param>
        object Execute(object value);
    }
}
